home *** CD-ROM | disk | FTP | other *** search
- Path: magnus.acs.ohio-state.edu!csn!ub!newserve!rebecca!rpi!not-for-mail
- From: hemal@maverick.corus.co.in (hemal pandya)
- Newsgroups: comp.lang.c++,comp.lang.c++.moderated
- Subject: Re: Pointer Typecasts
- Date: 2 Feb 1996 12:21:58 -0000
- Organization: ConnectSoft Ruksun Software India
- Sender: cppmods@netlab.cs.rpi.edu
- Approved: kanze@lts.sel.alcatel.de
- Message-ID: <4esvl6$imh@netlab.cs.rpi.edu>
- References: <4ejnr9$cpf@netlab.cs.rpi.edu>
- Reply-To: hemal@maverick.corus.co.in
- NNTP-Posting-Host: netlab.cs.rpi.edu
- X-Original-Date: Fri, 02 Feb 1996 04:03:57 GMT
-
- In article <4ejnr9$cpf@netlab.cs.rpi.edu> reckdahl@leland.Stanford.EDU
- (Keith Reckdahl) wrote:
-
- [details about problems with downcasting from virtual base
- class/multiply inherited base class]
-
- >What are we doing wrong?
-
- struct A {} ;
- struct B1 : A {} ;
- struct B2 : A {} ;
-
- struct D : B1, B2 {} ;
-
- Foo(A* a)
- {
- D* d2 = (D*) a ;
- }
-
- In article <4ejnr9$cpf@netlab.cs.rpi.edu> reckdahl@leland.Stanford.EDU
- (Keith Reckdahl) wrote:
-
- [problem about downcasting from virtual base class/multiply inherited
- base
- class]
-
- >What are we doing wrong?
-
- You are making mistake in both cases -- whether you use the virtual
- keyword
- or not. I will try to explain both of them separately.
-
- Lets us say your most base class is called A, classes B1 and B2 are
- derived
- from A and D is multiply derived from B1 and B2.
-
- class A { .... } ;
- class B2 : public A { .... } ;
- class B2 : public A { .... } ;
- class D : public B1, public B2 { .... } ;
-
- If you don't use the virtual inheritance, then, as you realize, you
- have two
- instances of class A for every object of type D. The layout of a D
- object
- might look like:
-
- +----------------------+
- | A-part of B1 |
- | |
- | B1 -part |
- |----------------------|
- | A-part of B2 |
- | |
- | B2-part |
- |----------------------|
- | D -part |
- | |
- | |
- +----------------------+
-
-
- One thing you should notice is that not all sub-objects of D can start
- at
- the same address, obviously :) Thus, when a pointer to D is converted
- to a
- pointer to B2, the bit-pattern of the two pointers will be different:
-
- D* d = new D ;
- B2* b2 = d ;
-
- long(d) != long(b2) ; // bit patterns of d and b2 are different
-
- This can be done (by the compiler) by "adding" to d the offset of the
- B2
- subobject in a D object.
-
- Conversely, to convert from a B2* to D* also, some arithmatic
- manipulation
- has to be performed. In the above case, the same offset will have to
- be
- subtracted from the B2* value. How is this done? Typically, the vtable
- contains the 'offset to the containing object' field. So given a A*
- (inside
- a B1) the D* can be calculated. For such a contained object, there is
- only
- one containing obhect, and a hierearchy of containment can be
- traversed to
- reach the desired object.
-
- Coming back to your original problem: converting a D* to an A* is
- ambiguous;
- there are two A's in a D. If a D* has to be converted to the A
- subobject of
- B1 then (according to this scheme) no arithmatic addition/subtraction
- needs
- to be done while if the case is meant to aquire a pointer to the A
- subobject
- of B2 the the addition has to be performed. Likewise, the same problem
- exists in converting a A* to a D* since it is not known which A the
- pointer
- points to.
-
- Putting in the intermediate base classes gets rid of the compiler
- error:
-
- A* a = (B1*) d ; // or (B2*) d;
-
- eliminates the amiguosity. SInce there is only one B1 in D, a cast to
- B1* is
- unambiguous and so is the cast from B1* to A*.
-
- Going in the other direction,
-
- D* d = (D*)(B1*) a ; // ot (D*)(B2*)a ;
-
- is unambiguous. But now we reach the catch: given an A* we have to
- know
- whther its the A part of B1 or B2. If this can be definitely known,
- then
- this the solution to your problem.
-
-
- But maybe this is not right. Im most simple cases, one is looking for
- a
- virtual inheritance in such cases:
-
- class A { .... } ;
- class B2 : public virtual A { .... } ;
- class B2 : public virtual A { .... } ;
- class D : public B1, public B2, public virtual A { .... } ;
-
- The layout might be:
-
- +----------------------+
- | |
- | B1 -part |
- |----------------------|
- | |
- | B2-part |
- |----------------------|
- | D -part |
- | |
- |----------------------|
- | A-part |
- +----------------------+
-
-
- Now there is only one A object in every D, casting from D* to A* is
- unambigous.
-
- A* a = d ; // conversion supllied by the compiler
-
- But downcasting is not so simple anymore. Essentially: the virtual
- base
- class object is "contained" , so to say, by all of B1, B2 and D. The
- offset
- to the containing object does not make sense in this case.
-
- Are you stuck? No. If your compiler supports RTTI, the following will
- convert from A* to D*:
-
- D* d = dynamic_cast<D*> a ;
-
- If the compiler does not support RTTI then you have to take longer
- route. The solution is as follows: define a virtual function in the
- base
- class which returns "this" as a void* and override it in every derived
- class. Then, use this function and a cast to do downcasting:
-
- class A
- {
- virtual void* Self { return this ; }
- } ;
-
- class B1 : public virtual A
- {
- virtual void* Self { return this ; }
- } ;
-
- class B2 : public virtual A
- {
- virtual void* Self { return this ; }
- } ;
-
- class D : public B1, public B2, virtual public A
- {
- virtual void* Self { return this ; }
- } ;
-
- A* a = d ;
- d = (D*) a->Self() ;
-
- This is quite ugly, but it works. Note that following does not work:
-
- D* d = (D*)(void*) a ;
-
- I leave it you to figure why the Self gimmick works.
-
- Hope this helps.
-
- >Keith Reckdahl, Paul Mitiguy, Margaret Becker
- >reckdahl@leland.stanford.edu
-
- - -Hemal
-
-
- [ Articles to moderate: mailto:c++-submit@netlab.cs.rpi.edu ]
- [ Read the C++ FAQ: http://www.connobj.com/cpp/cppfaq.htm ]
- [ Moderation policy: http://www.connobj.com/cpp/guide.htm ]
- [ Comments? mailto:c++-request@netlab.cs.rpi.edu ]
-